package com.heima.wemedia.service.impl;

import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.utils.common.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmNewsTaskService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper,WmNews> implements WmNewsService {
    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    //注入审核服务
    @Autowired
    private WmNewsAutoScanService wmNewsAutoScanService;

    @Autowired
    private WmNewsMapper wmNewsMapper;

    //注入延迟队列 添加文章发布任务
    @Autowired
    private WmNewsTaskService wmNewsTaskService;


    //查询自媒体文章
    @Override
    public ResponseResult findAll(WmNewsPageReqDto dto) {
 /*       //1.检查参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //分页参数检查
        dto.checkParam();
        //2.分页条件查询
        IPage page = new Page(dto.getPage(),dto.getSize());
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //状态精确查询
        if(dto.getStatus() != null){
            lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
        }
        //频道精确查询
        if(dto.getChannelId() != null){
            lambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        //时间范围查询
        if(dto.getBeginPubDate()!=null && dto.getEndPubDate()!=null){
            lambdaQueryWrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());
        }
        //关键字模糊查询
        if(StringUtils.isNotBlank(dto.getKeyword())){
            lambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());
        }

        //查询当前登录用户的文章
        lambdaQueryWrapper.eq(WmNews::getUserId,WmThreadLocalUtil.getUser().getId());
        //发布时间倒序查询
        lambdaQueryWrapper.orderByDesc(WmNews::getCreatedTime);
        page = page(page,lambdaQueryWrapper);
        //3.结果返回
        ResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());
        responseResult.setData(page.getRecords());
        return responseResult;*/

        //检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //分页参数检查校正
        dto.checkParam();

        //分页条件查询
        //构建分页参数
         IPage page = new Page(dto.getPage(), dto.getSize());

        //构建条件构造器对象
        LambdaQueryWrapper<WmNews> newsLambdaQueryWrapper = new LambdaQueryWrapper<>();

        //状态精准查询
        if (dto.getStatus() != null) {
            newsLambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
        }

        //频道精准查询
        if (dto.getChannelId() != null) {
            newsLambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        //时间范围查询
        if (dto.getBeginPubDate() != null && dto.getEndPubDate()!=null ){
            newsLambdaQueryWrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());
        }
        //关键字查询
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            //关键字模糊查询是like!!
            newsLambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());
        }

        //查询当前用户文章
        newsLambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());

        //发布时间倒序排序
        newsLambdaQueryWrapper.orderByAsc(WmNews::getCreatedTime);
        //将条件设置给分页查询对象
         page = page(page, newsLambdaQueryWrapper);
        //将结果返回
        PageResponseResult pageResponseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
        pageResponseResult.setData(page.getRecords());


        return pageResponseResult;
    }

    //发布文章方法,提交,修改,保存草稿
    /*
        1.前端提交发布或保存为草稿
        2.后台判断请求中是否包含了文章id
        3.如果不包含id,则为新增
        3.1 执行新增文章的操作
        3.2 关联文章内容图片与素材的关系
        3.3 关联文章封面图片与素材的关系
        4.如果包含了id，则为修改请求
        4.1 删除该文章与素材的所有关系
        4.2 执行修改操作
        4.3 关联文章内容图片与素材的关系
        4.4 关联文章封面图片与素材的关系
    * */
    @Override
    public ResponseResult submitNews(WmNewsDto dto) throws Exception {
        //检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //保存或修改文章
        WmNews wmNews = saveOrUpdateNews(dto);

        //判断文章提交的时候是直接提交还是保存草稿--如果是保存草稿则直接反馈
        if (dto.getStatus() == 0) {
            //草稿也是会保存在数据库文章表中但不会保存关联关系,提交后才会保存
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }

        //从内容中抽取所有的图片 存到一个集合当中
        List<String > imageUrls =  ectractUrlInfo(dto.getContent());

        //批量保存文章内容中的图片和素材之间的关系
        saveRelativeInfoForContent(imageUrls,wmNews.getId());

        //保存封面图片和素材之间的关系 -封面如果是自动的需要自己按照规则匹配图片然后再保存数据
        saveRelativeInfoForCover(dto.getImages(),imageUrls,wmNews,dto.getType());

        //注入审核服务调用审核方法 --添加审核任务到schedule延迟队列
        // wmNewsAutoScanService.autoScanWmNews(wmNews.getId());
        //调添加任务队列方法
        wmNewsTaskService.addWmNewsToTask(wmNews);



        //返回结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /* 1.如果文章封面选择自动，则自动匹配封面
     * 匹配规则：
        如果内容图片大于等于3  则是多图，截图3张图片作为封面
     *  如果内容图片大于等于1&&小于3  则是单图，截图1张图片作为封面
     *  如果内容没有图片，则是无图
     *
     * 2.保存封面图片与素材的关系*/
    private void saveRelativeInfoForCover(List<String> images, List<String> imageUrls, WmNews wmNews, Short type) {
        //对参数中传过来的封面类型类型做判断然后设置对应的规则
        if (type == -1) {
            //如果是自动的话那么就是用内容中的图片来做封面所以再判断内容中的图片情况
            if (imageUrls == null || imageUrls.size() == 0) {
                //内容中也没有图片那就设置文章为无图
                wmNews.setType((short)0);
            }else if (imageUrls.size() < 3){
                //如果小于3就 抽取一个图片作为封面 设置为单图
                images.add(imageUrls.get(0));
                wmNews.setType((short)1);
            }else {
                images.addAll(imageUrls.stream().limit(3).collect(Collectors.toList()));
                wmNews.setType((short)3);
            }
        }
        //然后进行数据更新//更新文章--更新文章的类型
        updateById(wmNews);

        //在保存关联关系前判断是否是无图状态 不是再做保存逻辑
        if ( null != images || images.size() > 0 ){
            saveRelativeInfo(images,wmNews.getId(),(byte) 1);

        }


    }

    //因关联关系的保存逻辑可通用 这里就直接抽取成一个方法然后调用
    private void saveRelativeInfo(List<String> imageUrls, Integer id, byte type) {

        //数据校验
        if (null == imageUrls || imageUrls.size() == 0) {
            return;

        }
        //图片集合里面可能存在相同的图片需要去重 然后把去重后的参数再赋值
        //集合中去重可以用stream中distinct方法去重
        imageUrls = imageUrls.stream().distinct().collect(Collectors.toList());

            //通过图片地址查询图片的id 使用条件构造器设置查询条件获取id集合对象
            //这里是先根据抽取出来的图片地址查询数据库中的地址然后在查id

            List<WmMaterial> materialList = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery()
                    .in(WmMaterial::getUrl, imageUrls));

            //判断数据是否被删除过了,因为在编写文章的过程中可能存在图片被删除的情况,如果是那就抛个异常错误提示
            if (materialList == null || materialList.size() != imageUrls.size()) {
                //素材没找到，或没找够被删了
                //抛出异常，并提示用户，素材引用失效
                throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            //通过news素材查询素材的id
            //遍历上面得到的图片id集合并添加到一个集合中
            //遍历用lambda表示
            List<Integer> materials = materialList.stream()
                    .map(wmMaterial -> wmMaterial.getId()).collect(Collectors.toList());

            //最后在中间表中保存对应的图片和素材关系
            wmNewsMaterialMapper.saveRelations(materials,id,type);

    }

    //批量保存文章内容中的图片和素材之间的关系
    private void saveRelativeInfoForContent(List<String> imageUrls, Integer wmNewsId) {

        saveRelativeInfo(imageUrls,wmNewsId,(byte)0);

    }

    //抽取内容中的所有图片地址然后保存
    private List<String> ectractUrlInfo(String content) {
        //因为传来的content是JSON数据格式的,需要转换为java对象并且是集合的形式保存
        //又因为对应的内容可以转换成key-value的格式所以这个转换的集合可以通过map的形式获取
        //1.将参数转换为集合并且是map类型的
        List<Map> list = JSON.parseArray(content, Map.class);
        //创建一个集合来装获取到的图片地址数据
        List<String> imagesUrls = new ArrayList<>();

        //判断是否为空,然后遍历获取对应的图片字段value值然后返回
        if (null != list || list.size()>0){
            //因为content中字段传输格式是 "type":"XXX","value":"xxxxx地址"转换为map类型后可以遍历K_V获取
            //这里就判断一下如果type = image 就表示是图片字段 然后遍历获取 value的值就是地址
            for (Map map : list) {
                if ("image".equals(map.get("type"))) {
                    //获取value值然后转化为String类型添加到集合中
                    imagesUrls.add(map.get("value").toString());
                }

            }
        }
        return imagesUrls;
    }

    ////保存或修改文章
    private WmNews saveOrUpdateNews(WmNewsDto dto) {
        //接受到的参数类型是dto但保存数据需要保存到-wmnews文章表，所以需要将参数进行拷贝 属性中不同的要进行转换
        WmNews wmNews = new WmNews();

        //将参数拷贝为wmNews类型对象
        BeanUtils.copyProperties(dto,wmNews);
        //图片处理
        //封面图片的数据属性不一致拷贝不会成功,需要我们自己处理-list转化为String
        //先判断参数情况
        if (null != dto.getImages() && dto.getImages().size()> 0) {
            //因为保存图片就要获取图片的地址来保存所以要将参数的格式进行转换
            //StringUtils中的join方法可以将前端参数中数组类型的参数做分割转换
            String images = StringUtils.join(dto.getImages().toArray(), ",");
            //将图片地址添加到wmNews中
            wmNews.setImages(images);
        }
        //判断dto中是否有id如果有则执行更新,如果没有则执行新增
        //这个是有id就表示之前文章就有了,再上传就是更新修改了
        //文章需要绑定用户作者id 就是此时登录进行文章发布的用户id,不然数据库里用户id为null
        wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
        if (dto.getId() == null) {
            //没有id存在就执行新增
            save(wmNews);
        }else {
            //如果都有就执行更新 但在执行更新前将原来的关系内容删除后再更新 在中键表中通过dto中携带的id来做删除关系
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,dto.getId()));
            updateById(wmNews);
        }

        return wmNews;
    }


    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    //文章上下架功能
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {

         //例行非空
        if (dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章ID不可缺少");
        }
        //根据id查询文章
        WmNews wmNews = wmNewsMapper.selectById(dto.getId());

        if (wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }

        if (wmNews.getStatus() != (short)9) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"当前文章不是发布状态.不能上下架");
        }

        //获取文章上下架状态
//        if (wmNews.getEnable() == (short)0 ) {
//            wmNews.setEnable((short)1);
//            wmNewsMapper.updateById(wmNews);
//        }else if (wmNews.getEnable() == (short)1){
//            wmNews.setEnable((short)0);
//            wmNewsMapper.updateById(wmNews);
//        }
        //修改文章enable
        if (dto.getEnable() != null && dto.getEnable() > -1 && dto.getEnable() <2) {
            update(Wrappers.<WmNews>lambdaUpdate().set(WmNews::getEnable,dto.getEnable())
                    .eq(WmNews::getId,wmNews.getId())
            );
//            wmNews.setEnable(dto.getEnable());
//            updateById(wmNews);
            if (wmNews.getArticleId() != null){
                //发送消息 修改文章配置
                Map<String, Object> map = new HashMap<>();
                map.put("articleId",wmNews.getArticleId());
                map.put("enable",dto.getEnable());
                kafkaTemplate.send("DownOrUp-topic", JSON.toJSONString(map));
            }
        }



        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

 /*       //检查参数
        if (dto == null || StringUtils.isBlank(dto.getContent())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //保存或修改文章
        WmNews wmNews = saveOrUpDateNews(dto);

        //判断文章提交的时候是直接提交还是保存草稿--如果是保存草稿则直接反馈
        if (dto.getStatus() == 0) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }

        //从内容中抽取所有的图片
        List<String> imageUrls = ectractUrlInfo(dto.getContent());

        //批量保存内容中的图片和素材之间的关系
        saveRelativeInfoForContent(imageUrls,wmNews.getId());

        //保存封面图片和素材之间的关系 -封面如果是自动的需要自己按照规则匹配图片然后再保存数据
        saveRelativeInfoForCover(dto.getImages(),imageUrls,wmNews,dto.getType());

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

        *//* 1.如果文章封面选择自动，则自动匹配封面
     * 匹配规则：
        如果内容图片大于等于3  则是多图，截图3张图片作为封面
     *  如果内容图片大于等于1&&小于3  则是单图，截图1张图片作为封面
     *  如果内容没有图片，则是无图
     *
     * 2.保存封面图片与素材的关系*/
    /*
    private void saveRelativeInfoForCover(List<String> images, List<String> imageUrls, WmNews wmNews, Short type) {
        //对参数中传过来的封面类型类型做判断然后设置对应的规则
        if (type == -1) {
            if (imageUrls == null || imageUrls.size() == 0) {
                wmNews.setType((short)0);
            }else if (imageUrls.size() < 3){
                //抽取一个图片作为封面
                images.add(imageUrls.get(0));
                wmNews.setType((short)1);
            } else {
                //如果是多图就直接用三张图片作为封面
                images.addAll(imageUrls.stream().limit(3).collect(Collectors.toList()));
                wmNews.setType((short)3);
            }
        }
        //更新文章--更新文章的类型
        updateById(wmNews);

        //在保存关联关系前判断是否是无图状态 不是再做保存逻辑
        if ( null != images || images.size() > 0 ){
            saveRelativeInfo(images,wmNews.getId(),(byte) 1);

        }
    }

    //因关联关系的保存逻辑可通用 这里就直接抽取成一个方法然后调用
    public void saveRelativeInfo(List<String> imageUrls,Integer wmNewsId ,byte type){
        //图片集合里面可能存在相同的图片需要去重 然后把去重后的参数再赋值
        imageUrls = imageUrls.stream().distinct().collect(Collectors.toList());
        //通过图片地址查询图片的id 使用条件构造器设置查询条件获取id集合对象
        List<WmMaterial> list = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery()
                .in(WmMaterial::getUrl, imageUrls));

        //判断数据是否被删除过了,因为在编写文章的过程中可能存在图片被删除的情况,如果是那就返回错误提示
        if ( list == null || list.size() != imageUrls.size()) {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //通过素材查询素材的id
        //遍历上面得到的图片id并添加到一个集合中
      */
    /*  List<Integer> matertial = new ArrayList<>();
        for (WmMaterial wmMaterial : list) {
            matertial.add(wmMaterial.getId());
        }*//*
        //遍历用lambda表示
        List<Integer> materialIds = list.stream()
                .map(wmMaterial -> wmMaterial.getId()).collect(Collectors.toList());
        //最后在中间表中保存对应的图片和
        // 素材关系
        wmNewsMaterialMapper.saveRelations(materialIds,wmNewsId,type);

    }

    // 保存文章内容图片与素材的关系
    private void saveRelativeInfoForContent(List<String> imageUrls, Integer id) {
        saveRelativeInfo(imageUrls,id,(byte) 0);
    }
    //抽取文章内容content里面的所有图片
    //

    private List<String> ectractUrlInfo(String content) {
        //因为传来的content是JSON数据格式的,需要转换为java对象并且是集合的形式保存
        //又因为对应的内容可以转换成key-value的格式所以这个转换的集合可以通过map的形式获取
        //1.将参数转换为集合并且是map类型的
        List<Map> list = JSON.parseArray(content, Map.class);

        //创建一个集合来装获取到的图片地址数据
        List<String> result = new ArrayList<>();

        //遍历获取对应的图片字段value值然后返回
        for (Map map : list) {
            //因为content中字段传输格式是 "type":"XXX","value":"xxxxx地址"转换为map类型后可以遍历K_V获取
            //这里就判断一下如果type = image 就表示是图片字段 然后遍历获取 value的值就是地址
            if ("image".equals(map.get("type"))){
                //获取value值然后转化为String类型添加到集合中
                String value = map.get("value").toString();
                result.add(value);
            }
        }
        return result;
    }


    //保存或修改文章方法
    private WmNews saveOrUpDateNews(WmNewsDto dto) {
        //接受到的参数类型是dto但保存数据需要保存到-wmnews文章表，所以需要将参数进行拷贝 属性中不同的要进行转换
        WmNews wmNews = new WmNews();
        //属性拷贝把dto中数据拷贝到wmNews对象
        //两个对象的属性必须一致
        //两个对象的属性类型必须一致
        BeanUtils.copyProperties(dto,wmNews);

        //图片处理
        //封面图片的数据属性不一致拷贝不会成功,需要我们自己处理-list转化为String
        //先判断参数情况
        if (dto.getImages() !=null && dto.getImages().size() > 0) {
            //从集合转换为字符串--这个StringUtils要注意jar包冲突
            String images = StringUtils.join(dto.getImages().toArray(), ",");
            wmNews.setImages(images);

        }
        *//*
        //如果选择自动,-1 存储报错,暂时将类型修改为null,后面补
        else if (dto.getType() == -1) {
                wmNews.setType(null);
            }*//*

        //判断dto中是否有id如果有则执行更新,如果没有则执行新增
        if (StringUtils.isNotEmpty(dto.getContent()) && dto.getId() == null) {
            save(wmNews);
        }else {
            //如果都有就执行更新 在执行更新前将原来的关系内容删除后再更新
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId,dto.getId()));
            updateById(wmNews);
        }

        return wmNews;
    }*/

}
